perm filename FREEFO.LES[UP,DOC]3 blob
sn#056649 filedate 1973-08-03 generic text, type T, neo UTF8
COMMENT ⊗ VALID 00003 PAGES
RECORD PAGE DESCRIPTION
00001 00001
00002 00002 BEGIN "FREEFOROL"
00008 00003 DEFINE NOVA="25" COMMENT MAXIMUM NUMBER OF STRING SUBSTITUTIONS
00013 ENDMK
⊗;
BEGIN "FREEFOROL"
COMMENT 10 MARCH 1973
OPERATION
Freeforol is a text macro processor that can be used to generate form
letters and other fill-in-the-blanks text. If you say R FREEFO, it
types a "*" and expects a keyboard input of the form
<source file list>
or
<destination file>←<source file list>
where the <source file list> consists of one or more file names
separated by commas. These must be text files and will be
effectively concatenated in the order given.
The output goes to the destination file, if given. Otherwise one
copy of the output is spooled.
PROCESS
The source file(s) are expected to contain a text macro followed by
one or more argument lists. This program outputs a copy of the macro
for each arguments list, with arguments substituted.
The first character of the source file (neglecting any directory
page or line numbers) is taken as the macro delimiter. The macro
begins just after this. Each place where an argument is to be
substituted is designated by a <delimiter><parameter> pair, where
the <parameter> may be "1" through "9" (representing the first
through ninth arguments) or "A" through "J" (10th through 20th
arguments). The macro ends with
<delimiter><separator><delimeter>
where the <separator> is any non-<parameter>.
The argument lists begin on the next line, which can of course be in
a different file if you wish. Either of two formats may be used, as
follows.
1) If the <separator> is <CR> or <CR><LF>, then each line of text is
interpreted as an argument and the list is terminated by a blank
line. This format is convenient for lists of addressees that are to
be inserted in a form letter.
2) If the separator is any other character, then each line of text is
interpreted as a list whose arguments are separated by the
<separator>.
Thus, the general form of the source file(s) is
<delimiter>
<macro body, including parameters>
<delimiter><separator><delimiter>
<argument lists>
EXAMPLES
For example, suppose the following source file were used:
_____________________________________________________________________
⊗ Veterans Administration
Washington, D. C.
4 July 1984
⊗1 ⊗2
⊗3
⊗4
Dear ⊗1:
Our office has recently determined that you qualify for a 10%
disability. Congratulations ...
⊗
⊗
John
Brown
Arlington National Cemetery
Arlington, Virginia
Speedy
Gonzales
Blue Fox Restaurant
Tijuana, Mexico
_____________________________________________________________________
Then "⊗" is the delimiter and the separator is <CR><LF>, so there
would be two copies of the letter generated, with the first one
addressed:
John Brown
Arlington National Cemetery
Arlington, Virginia
Dear John:
....
Alternatively, if the source file were:
_____________________________________________________________________
@
This is a@2 macro processor,
but it is @1.
@,@
fairly general, simple
rather easy to understand,n elegant
_____________________________________________________________________
then "@" is the delimiter and "," is the separator, so this produces:
This is a simple macro processor,
but it is fairly general.
This is an elegant macro processor,
but it is fairly easy to understand.
FEATURES
Several features should be noted.
1) Any form feeds in the macro body are preserved, but they are
ignored in the argument lists.
2) Excess or unused arguments are ignored, so you can put comments
in the argument lists that will not show up in the output.
3) Arguments that are omitted are automatically set to NULL.
;
DEFINE NOVA="25"; COMMENT MAXIMUM NUMBER OF STRING SUBSTITUTIONS;
DEFINE ARGS="20"; COMMENT THE MAXIMUM NUMBER OF AGRUMENTS;
DEFINE THIS="COMMENT", CR="'15", LF="'12", HT="'11", FF="'14",↓="'15&'12",
THRU="STEP 1 UNTIL", ⊃="←1 STEP 1 UNTIL", LN="LENGTH",
ERR(MES)="BEGIN OUTSTR(""MES""&↓); CALL(0,""EXIT"") END";
DEFINE INCH="1", OUCH="2", LINE="1", MARK="2", CHAR="3",AMP="4",FORMER="5";
REQUIRE 2000 STRING_SPACE;
STRING ARRAY TEXT[1:NOVA]; THIS HOLDS SEGMENTS OF MACRO TEXT;
INTEGER ARRAY PARG[1:NOVA];
STRING ARRAY VAL[1:ARGS]; THIS HOLDS CURRENT ARGUMENT VALUES;
STRING S,S0,SOURCE,DEST;
INTEGER BRK, EOF,T, V, I,FLAG;
EXTERNAL PROCEDURE SPOOL(STRING FILE; INTEGER IOCHAN,FLAGS);
PROCEDURE LOOK; BEGIN BOOLEAN FL; STRING FILE;
FILE←SCAN(SOURCE,AMP,BRK);
IF BRK="[" THEN FILE←FILE&"["&SCAN(SOURCE,AMP,BRK)&","&SCAN(SOURCE,AMP,BRK);
LOOKUP(INCH,FILE,FL);
IF FL THEN BEGIN OUTSTR(FILE&" not found"&↓); CALL(0,"EXIT") END;
END;
STRING SIMPLE PROCEDURE READ; BEGIN THIS READS THE SOURCE FILE(S);
STRING RS,SS; RS←INPUT(INCH,CHAR);
IF ¬EOF ∨ LN(SOURCE)=0 THEN RETURN(RS);
CLOSE(INCH); LOOK; SS←INPUT(INCH,CHAR);
IF EQU(SS[1 TO 9],"COMMENT ⊗") THEN BEGIN "TV FILE"
DO SS←INPUT(INCH,FORMER) UNTIL BRK=FF ∨ EOF;
SS←INPUT(INCH,CHAR);
END;
RETURN(RS&SS)
END;
THIS INITIALIZES I/O;
OUTSTR("*");
OPEN(INCH,"DSK",1,2,0,4000,BRK,EOF); OPEN(OUCH,"DSK",1,0,2,0,BRK,EOF);
SETBREAK(AMP,"←",NULL,"INS"); SETBREAK(FORMER,FF,NULL,"INS");
DEST←SCAN(SOURCE←INCHWL,AMP,BRK); THIS READS THE FILE NAMES;
IF BRK="←" THEN BEGIN "destination file"
ENTER(OUCH,DEST,FLAG); DEST←NULL;
END
ELSE BEGIN "spool it"
SOURCE←DEST; THIS RESTORES SOURCE FILE NAMES;
DEST←"FREEF"; I←"0"-1;
DO BEGIN LOOKUP(OUCH,DEST&(I←I+1)&".TMP",FLAG); CLOSE(OUCH) END
UNTIL FLAG;
ENTER(OUCH,DEST←DEST&I&".TMP",FLAG);
END;
IF FLAG THEN ERR(Destination file cannot be written);
SETBREAK(AMP,"[,"," ","INS"); LOOK;
THIS READS AND SEGMENTS THE TEXT MACRO;
SETBREAK(CHAR,NULL,NULL,"XNA");
IF (I←READ)="C" THEN BEGIN "FLUSH DIRECTORY"
DO I←INPUT(INCH,FORMER) UNTIL BRK=FF ∨ EOF;
I←READ;
END;
SETBREAK(CHAR,I,NULL,"INS");
S0←READ; T←0;
DO V←LOP(TEXT[T←T+1]←READ) UNTIL (I←PARG[T]←V-(IF "0"<V≤"9" THEN "0"
ELSE IF "A"≤V THEN "A"-10 ELSE "A"))≤0 ∨ I>ARGS;
THIS FINDS THE SEPARATORS AND TERMINATOR;
SETBREAK(MARK,V,NULL,"INS"); SETBREAK(CHAR,LF,CR&FF,"INS");
I←READ; THIS FLUSHES THE REST OF THE LINE;
DO BEGIN THIS SCANS ARGUMENT LISTS AND OUTPUTS TEXT;
IF V=CR THEN BEGIN "1 ARGUMENT PER LINE"
FOR I ⊃ ARGS DO IF LN(VAL[I]←READ)=0 THEN DONE;
END
ELSE BEGIN "SET OF ARGUMENTS PER LINE"
S←READ;
FOR I ⊃ ARGS DO IF LN(VAL[I]←SCAN(S,MARK,BRK))=0 THEN DONE;
END;
IF EOF ∧ I=1 THEN DONE;
FOR I←I THRU ARGS DO VAL[I]←NULL;
OUT(OUCH,S0);
FOR I ⊃ T-1 DO BEGIN OUT(OUCH,VAL[PARG[I]]); OUT(OUCH,TEXT[I]) END;
END UNTIL EOF;
RELEASE(INCH); RELEASE(OUCH);
IF LN(DEST) THEN SPOOL(OUCH,DEST,1);
END;